#ifndef __MAT4_H__
#define __MAT4_H__
#include "vec4.h"

class Mat4x4 {
public:
    Mat4x4() = default;
    Mat4x4(std::array<float, 16>&& list)
        : data(std::move(list)) { }

    // 按行优先访问 
    // r -> 行, c -> 列
    // mat(2, 3) -> mat.data[2 * 4 + 3] -> mat.data[11]
    float& operator () (const int r, const int c) {
        return data[r * col + c];
    }
    float operator () (const int r, const int c) const {
        return data[r * col + c];
    }

    // 按行优先遍历
    std::array<float, 16>::iterator begin() {
        return data.begin();
    }
    std::array<float, 16>::iterator end() {
        return data.end();
    }

    std::array<float, 16>::const_iterator cbegin() const {
        return data.cbegin();
    }
    std::array<float, 16>::const_iterator cend() const {
        return data.cend();
    }

    static Mat4x4 One() {
        return Mat4x4({
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
        });
    }

    static Mat4x4 Zero() {
        return Mat4x4({
            0, 0, 0, 0,
            0, 0, 0, 0,
            0, 0, 0, 0,
            0, 0, 0, 0
        });
    }
    
    Mat4x4& operator += (const Mat4x4& other) {
        *this = *this + other;
        return *this;
    }

    Mat4x4& operator -= (const Mat4x4& other) {
        *this = *this + other;
        return *this;
    }

    Mat4x4& operator *= (const Mat4x4& other) {
        *this = *this * other;
        return *this;
    }

    Mat4x4& operator *= (float value) {
        *this = *this * value;
        return *this;
    }

    friend Mat4x4 operator + (const Mat4x4& m1, const Mat4x4& m2);
    friend Mat4x4 operator - (const Mat4x4& m1, const Mat4x4& m2);
    friend Mat4x4 operator * (const Mat4x4& m1, float value);
    friend Mat4x4 operator * (float value, const Mat4x4& m1);
    friend Mat4x4 operator * (const Mat4x4& m1, const Mat4x4& m2);
    friend Vec4f operator * (const Mat4x4& m1, const Vec4f& v1);
    friend Mat4x4 operator / (const Mat4x4& m1, float value);
public:
    static constexpr int col = 4;
    static constexpr int row = 4;
private:
    std::array<float, 16> data;
};

extern Mat4x4 transpose(const Mat4x4& mat);

#endif